#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "PlayerSelectDlg.h"
#include "TurnScoreSelectDlg.h"
#include "NewPlayerDlg.h"

#include "Game.h"
#include "Player.h"

#include <QFile>
#include <QDebug>
#include <QList>
#include <QMap>
#include <cmath>
#include <QPushButton>
#include <QCloseEvent>
#include <QFileDialog>

#define lg(x) log(x)/log(2)

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  setWindowTitle("4 Ball Tournament Generator");
  setWindowIcon(QIcon(QPixmap("icon.png")));


  /* To regnerate the rankings file.
  setupRosterTable();
  readTournamentFile("4BallTournaments.4blt");
  scoreRoster(mTournaments);
  addRosterToTable(mRoster);
  writeRosterFile("4BallTempRatings.4bl");
  */

  setupConnections();
  setupRosterTable();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setupConnections()
{
  //Local Signals
  connect(ui->selectAllRosterBtn, SIGNAL(clicked()), this, SLOT(onSelectAll()));
  connect(ui->clearSelectedRosterBtn, SIGNAL(clicked()), this, SLOT(onClearSelected()));
  connect(ui->generateBracketRosterBtn, SIGNAL(clicked()), this, SLOT(onGenerateBracket()));

  // Menu Actions
  connect(ui->actionNew_Roster, SIGNAL(triggered()), this, SLOT(onNewRoster()));
  connect(ui->actionOpen_Roster, SIGNAL(triggered()), this, SLOT(onOpenRoster()));
  connect(ui->actionSave_Roster, SIGNAL(triggered()), this, SLOT(onSaveRoster()));
  connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(onQuit()));
  connect(ui->actionAdd_Player, SIGNAL(triggered()), this, SLOT(onAddPlayer()));
  connect(ui->actionRemove_Player, SIGNAL(triggered()), this, SLOT(onRemovePlayers()));
}

void MainWindow::loadRosterFile(const QString &filename)
{
  mRoster.clear();
  mTournaments.clear();
  if (readRosterFile(filename))
  {
    addRosterToTable(mRoster);
  }
}

bool MainWindow::readRosterFile(const QString &file)
{
  QFile rfile(file);
  rfile.open(QFile::ReadOnly);
  if (rfile.isOpen())
  {
    while (!rfile.atEnd())
    {
      QString playerstr(rfile.readLine());
      playerstr.replace("\r\n", "");
      Player p("INVALID");
      p.fromString(playerstr);
      mRoster.insert(p.name(), p);
    }
    return true;
  }
  return false;
}

bool MainWindow::readTournamentFile(const QString &file)
{
  QFile tfile(file);
  tfile.open(QFile::ReadOnly);
  if (tfile.isOpen())
  {
    while(!tfile.atEnd())
    {
      Tournament t;
      QString text(tfile.readLine());
      int numPlayers(0);
      QList<QString> tokens(text.split(","));
      if (tokens.count() > 0)
      {
        numPlayers = tokens.at(0).toInt();
        for (int i=1; i<= numPlayers; ++i)
        {
          QString name(tokens.at(i));
          name = name.replace("\r\n","");
          t.mRoster.insert(name, Player(name));
          if (!mRoster.contains(name))
          {
            mRoster.insert(name, Player(name));
          }
        }
      }

      static int tny(1);
      qDebug() << "Tournament" << tny++;
      foreach (Player p, t.mRoster)
      {
        qDebug() << "  " << p.toString();
      }

      int winningbracketgames(numPlayers-1),
          losingbracketgames(numPlayers-2);
      for (int i(0); i<winningbracketgames; ++i)
      {
        QString gamestr(tfile.readLine());
        QList<QString> tokens2(gamestr.split(","));
        if (tokens2.count() > 3)
        {
          QString p1(tokens2.at(0)), p2(tokens2.at(1));
          if (t.mRoster.contains(p1) && t.mRoster.contains(p2))
          {
            Game g(tokens2);
            t.mWinningGames << g;
            t.mRoster.find(p1)->adjustRating(*(t.mRoster.find(p2)), t.mRoster.find(p1)->addGame(g), 10);
            t.mRoster.find(p2)->adjustRating(*(t.mRoster.find(p1)), t.mRoster.find(p2)->addGame(g), 10);
          }
        }
      }

      for (int i(0); i<losingbracketgames; ++i)
      {
        QString gamestr(tfile.readLine());
        QList<QString> tokens2(gamestr.split(","));
        if (tokens2.count() > 3)
        {
          QString p1(tokens2.at(0)), p2(tokens2.at(1));
          if (t.mRoster.contains(p1) && t.mRoster.contains(p2))
          {
            Game g(tokens2);
            t.mLosingGames << g;
            t.mRoster.find(p1)->adjustRating(*(t.mRoster.find(p2)), t.mRoster.find(p1)->addGame(g), 5);
            t.mRoster.find(p2)->adjustRating(*(t.mRoster.find(p1)), t.mRoster.find(p2)->addGame(g), 5);
          }
        }
      }
      mTournaments << t;
    }
    return true;
  }
  return false;
}

void MainWindow::addRosterToTable(const QMap<QString, Player> &roster)
{
  foreach(Player p, roster)
    addPlayerToRosterTable(p);
}

void MainWindow::addPlayerToRosterTable(const QString &name, float rating, int games, int rounds, int bagsmade, int points)
{
  Player p(name);
  p.setRating(rating);
  p.setGames(games);
  p.setRoundsPlayed(rounds);
  p.setBagsMade(bagsmade);
  p.setPointsScored(points);
  addPlayerToRosterTable(p);
}

void MainWindow::addPlayerToRosterTable(const Player &player)
{
  int r(ui->rosterView->rowCount());
  ui->rosterView->insertRow(r);
  ui->rosterView->setItem(r,0,new QTableWidgetItem(player.name()));
  ui->rosterView->setItem(r,1,new TableWidgetNumberItem(player.rating()));
  ui->rosterView->setItem(r,2,new TableWidgetNumberItem(player.games()));
  ui->rosterView->setItem(r, 3, new TableWidgetNumberItem(player.rounds()));
  ui->rosterView->setItem(r,5,new TableWidgetNumberItem(player.ballShotPct()*100));
  ui->rosterView->setItem(r,4,new TableWidgetNumberItem(player.bagShotPct()*100));
  ui->rosterView->setItem(r,6,new TableWidgetNumberItem(player.pointsScored()));
}

void MainWindow::setupRosterTable()
{
  QList<QString> labels;
  labels << "Name" << "Rating" << "Games" << "Rounds" << "Ball-Shot%" << "Bag-Shot%" << "Points";
  ui->rosterView->setColumnCount(7);
  ui->rosterView->setHorizontalHeaderLabels(labels);
}

void MainWindow::clearRosterTable()
{
  ui->rosterView->clearContents();
  ui->rosterView->setRowCount(0);
}

void MainWindow::scoreRoster(const QList<Tournament> &tournaments)
{
  foreach(Tournament t, tournaments)
  {
    foreach(Game g, t.mWinningGames)
    {
      if (mRoster.contains(g.player1()) &&
          mRoster.contains(g.player2()))
      {
        mRoster.find(g.player1())->adjustRating(*mRoster.find(g.player2()),
                                              mRoster.find(g.player1())->addGame(g), 10);
        mRoster.find(g.player2())->adjustRating(*mRoster.find(g.player1()),
                                              mRoster.find(g.player2())->addGame(g), 10);
      }
    }
    foreach(Game g, t.mLosingGames)
    {
      if (mRoster.contains(g.player1()) &&
          mRoster.contains(g.player2()))
      {
        mRoster.find(g.player1())->adjustRating(*mRoster.find(g.player2()),
                                              mRoster.find(g.player1())->addGame(g), 5);
        mRoster.find(g.player2())->adjustRating(*mRoster.find(g.player1()),
                                              mRoster.find(g.player2())->addGame(g), 5);
      }
    }
  }
}

void MainWindow::writeRosterFile(const QString &filename) const
{
  QFile rfile(filename);
  rfile.open(QFile::WriteOnly);
  if (rfile.isOpen())
  {
    foreach(Player p, mRoster)
    {
      rfile.write(p.toFileString().toStdString().c_str());
      rfile.write("\r\n");
    }
    rfile.close();
  }
}

QPushButton* MainWindow::createGameBtn(int x, int y, int w, int h, const QString &p1,
                               const QString &p2, bool bye, int r, int g)
{
  QPushButton *b(new QPushButton(ui->bracketWidget));
  if (bye)
  {
    b->setText(p1);
    b->setEnabled(false);
  }
  else
  {
    b->setText(p1+"\r\n"+p2);
    b->setEnabled(r == 1);
  }
  b->setObjectName(QString("Game%0_%1").arg(r).arg(g));
  b->resize(w, h);
  b->move(x, y);

  connect(b, SIGNAL(clicked()), this, SLOT(onBracketGameSelected()));
  return b;
}

void MainWindow::generateBracket(QList<Player>& players)
{
  // Order the players
  int w(50), h(50), s(10);
  if (!mCurrentTournament.mLosingPhase)
  {
    qSort(players.begin(), players.end(), qGreater<Player>());
  }
  qDebug() << "Making a bracket with" << players.count() << "players.";
  mCurrentTournament.mNumWinningGames = players.count()-1;
  mCurrentTournament.mNumWinningByes = pow(2, ceil(lg(players.count())))-players.count();

  qDebug() << "Games in bracket=" << mCurrentTournament.mNumWinningGames;
  qDebug() << "Byes in bracket=" << mCurrentTournament.mNumWinningByes;

  // Generate the first round games
  int frg((players.count()-mCurrentTournament.mNumWinningByes)
          /2+mCurrentTournament.mNumWinningByes);
  bool lastPlayerBye(false), byevsbye(false);
  ui->bracketWidget->setMinimumSize(ui->bracketWidget->width(),
                                    s+frg*(s+w));

  qDebug() << "First round games=" << frg;
  QList<Player> byes;
  for (int i(0); i<mCurrentTournament.mNumWinningByes; ++i)
  {
    byes << players.takeFirst();
  }

  int p1(players.count()/2-1),
      p2(p1+1);
  for (int i(0); i<frg; ++i)
  {
    if (byes.count() > 0 && !lastPlayerBye)
    {
      if (byevsbye)
      {
        mCurrentTournamentGames <<
          createGameBtn(s, s+i*(s+w), w, h, byes.takeLast().name(), "", true, 1, i+1);
      }
      else
      {
        mCurrentTournamentGames <<
          createGameBtn(s, s+i*(s+w), w, h, byes.takeFirst().name(), "", true, 1, i+1);
      }
      lastPlayerBye = true;
    }
    else if (p1 >= 0)
    {
      mCurrentTournamentGames <<
        createGameBtn(s, s+i*(s+w), w, h, players.at(p1).name(), players.at(p2).name(), false, 1, i+1);
      --p1;
      ++p2;
      lastPlayerBye = false;
      byevsbye = false;
    }
    else
    {
      lastPlayerBye = false;
      --i;
      byevsbye = !byevsbye;
    }
  }

  // Generate every round after the first.
  int r(2);
  frg /= 2;
  int x(2*s+w);
  while (frg > 0)
  {
    for (int i(1); i<=frg; ++i)
    {
      QPushButton *g1(ui->bracketWidget->findChild<QPushButton*>(QString("Game%0_%1").arg(r-1).arg(i*2-1))),
                  *g2(ui->bracketWidget->findChild<QPushButton*>(QString("Game%0_%1").arg(r-1).arg(i*2)));
      int y((g1->pos().y()+g2->pos().y())/2);
      QString f("?"), l("?");
      // Fill in second round games with first round bye players
      if (r == 2)
      {
        if (!g1->text().contains("\r\n"))
        {
          f = g1->text();
        }
        if (!g2->text().contains("\r\n"))
        {
          l = g2->text();
        }
      }
      QPushButton *g(createGameBtn(x, y, w, h, f, l, false, r, i));
      if (f != "?" && l != "?") g->setEnabled(true);
      mCurrentTournamentGames << g;
    }
    x += (s+w);
    frg /= 2;
    ++r;
  }
}

void MainWindow::closeEvent(QCloseEvent *e)
{
  qApp->quit();
}

void MainWindow::onNewRoster()
{
  clearRosterTable();
}

void MainWindow::onOpenRoster()
{
  QString filename = QFileDialog::getOpenFileName(this, "Select a Roster", QDir::currentPath(), QString("4 Ball Rosters (*.4bl)"));
  if (!filename.isEmpty())
  {
    mTournaments.clear();
    mRoster.clear();
    if (readRosterFile(filename))
    {
      clearRosterTable();
      addRosterToTable(mRoster);
    }
  }
}

void MainWindow::onSaveRoster()
{
  QString filename = QFileDialog::getSaveFileName(this, "Save this Roster", QDir::currentPath(), QString("4 Ball Rosters (*.4bl)"));
  writeRosterFile(filename);
}

void MainWindow::onQuit()
{
  qApp->quit();
}

void MainWindow::onAddPlayer()
{
  QString name(NewPlayerDlg::getNewPlayerName(this));
  if (name != "" && !mRoster.contains(name))
  {
    mRoster.insert(name, Player(name));
    addPlayerToRosterTable(*mRoster.find(name));
  }
}

void MainWindow::onRemovePlayers()
{
  QList<QTableWidgetItem*> items(ui->rosterView->selectedItems());
  foreach (QTableWidgetItem *twi, items)
  {
    if (twi->column() == 0 && mRoster.contains(twi->text()))
    {
      mRoster.remove(twi->text());
    }
  }

  clearRosterTable();
  addRosterToTable(mRoster);
}

void MainWindow::onSelectAll()
{
  ui->rosterView->selectAll();
  ui->rosterView->setFocus(Qt::OtherFocusReason);
}

void MainWindow::onClearSelected()
{
  ui->rosterView->clearSelection();
}

void MainWindow::onGenerateBracket()
{
  // Get the selected players
  QList<QTableWidgetItem*> items(ui->rosterView->selectedItems());
  mCurrentTournament = Tournament();
  mCurrentTournament.mLosingPhase = false;
  QList<Player> players;
  foreach(QTableWidgetItem *twi, items)
  {
    if (twi->column() == 0 && mRoster.contains(twi->text()))
    {
      mCurrentTournament.mRoster.insert(twi->text(), *mRoster.find(twi->text()));
      players << *mRoster.find(twi->text());
    }
  }

  if (mCurrentTournament.mRoster.count() > 1)
  {
    // Remove the old bracket
    foreach (QPushButton *b, mCurrentTournamentGames)
    {
      b->setParent(NULL);
      delete b;
    }
    mCurrentTournamentGames.clear();
    ui->gameView->clear();

    generateBracket(players);
    ui->bracketLabel->setText("Winner's Bracket");
    ui->tabWidget->setCurrentIndex(2);
  }
}

void MainWindow::onBracketGameSelected()
{
  QPushButton *gbtn(dynamic_cast<QPushButton*>(sender()));
  if (gbtn != NULL)
  {
    QString p1(gbtn->text().split("\r\n").at(0)),
            p2(gbtn->text().split("\r\n").at(1));
    p1 = p1.replace("\r\n", "");
    QString firstplayer(PlayerSelectDlg::getFirstPlayer(this, p1, p2));
    if (!firstplayer.isEmpty())
    {
      Game g(TurnScoreSelectDlg::getRound(this, p1==firstplayer?p1:p2, p1==firstplayer?p2:p1));
      if (!g.isNull())
      {
        int scoreAdjust(10);
        if (!mCurrentTournament.mLosingPhase)
        {
          mCurrentTournament.mWinningGames << g;
          mCurrentTournament.mLosers << *(mCurrentTournament.mRoster.find(g.loser()));
        }
        else
        {
          scoreAdjust = 5;
          mCurrentTournament.mLosingGames << g;
        }
        Player &player1(*(mCurrentTournament.mRoster.find(p1))),
               &player2(*(mCurrentTournament.mRoster.find(p2)));
        player1.addGame(g);
        player2.addGame(g);
        player1.adjustRating(player2, player1.name()==g.winner(), scoreAdjust);
        player2.adjustRating(player1, player2.name()==g.winner(), scoreAdjust);
        QString summary(g.toString() + "\r\n  "+
                        player1.name()+"'s rating: "+QString::number(player1.rating()) + "\r\n  "+
                        player2.name()+"'s rating: "+QString::number(player2.rating()) + "\r\n");
        ui->gameView->addItem(new QListWidgetItem(summary));
        gbtn->setEnabled(false);
        QString gamestr(gbtn->objectName().remove(0, 4));
        int round, game;
        char t;
        sscanf(gamestr.toStdString().c_str(), "%d%c%d", &round, &t, &game);
        QString nextGame(QString("Game%0_%1").arg(round+1).arg((game+1)/2));
        QPushButton *nextGameBtn(ui->bracketWidget->findChild<QPushButton*>(nextGame));
        if (nextGameBtn != NULL)
        {
          QString f(nextGameBtn->text()),
                  l(nextGameBtn->text());
          QString fn(f.remove(f.indexOf("\r\n"), f.length()).replace("\r\n", "")),
                  ln(l.remove(0, l.indexOf("\r\n")).replace("\r\n", ""));
          if (game%2==1)
          {
            nextGameBtn->setText(g.winner() + "\r\n" + ln);
          }
          else
          {
            nextGameBtn->setText(fn + "\r\n" + g.winner());
          }
          nextGameBtn->setEnabled(!nextGameBtn->text().contains("?"));
        }
        // If we don't have a next game button, we can assume this bracket is finished.
        else if (!mCurrentTournament.mLosingPhase)
        {
          mCurrentTournament.mLosingPhase = true;
          foreach (QPushButton *b, mCurrentTournamentGames)
          {
            b->setParent(NULL);
            delete b;
          }
          mCurrentTournamentGames.clear();

          ui->gameView->addItem(new QListWidgetItem("Winner: " + g.winner()));
          ui->gameView->addItem(new QListWidgetItem("Losers Bracket"));

          mCurrentTournament.mFinalsLoser = mCurrentTournament.mLosers.takeLast();
          generateBracket(mCurrentTournament.mLosers);
          ui->bracketLabel->setText("Loser's Bracket");
        }
      }
    }
  }
}
